/*
 * Copyright 2010 WANdisco, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */
package com.googlecode.svnj.dav;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.servlet.ServletRequest;

import org.apache.jackrabbit.webdav.DavResourceLocator;
import org.apache.jackrabbit.webdav.DavServletRequest;
import org.apache.jackrabbit.webdav.lock.LockManager;
import org.apache.jackrabbit.webdav.lock.SimpleLockManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.googlecode.svnj.RepoContext;

public class IO {
    public static final String SYSPROP_PARENT_PATH = "svnj.parentPath";
    public static final String SYSPROP_REPO_PREFIX = "svnj.repoPrefix";
    /**
     * the default logger
     */
    private static final Logger log = LoggerFactory.getLogger(IO.class);
    // context path + servlet path of this web app
    // used as the 'repository' path in the webDAV implementation
    private SVNLocatorFactory locatorFactory;
    private static IO instance;
    private SimpleLockManager lockManager;
    
    private IO() {
        lockManager = new SimpleLockManager();
        locatorFactory = new SVNLocatorFactory();
    }
    
    /* initialize from system properties */
    static {
        String svnParentPath = System.getProperty(SYSPROP_PARENT_PATH);
        
        if (svnParentPath == null) {
            throw new RuntimeException(
                    SYSPROP_PARENT_PATH  + " system prop not specified"
                    );
        }
        File svnParentDir = new File(svnParentPath);
        if (!svnParentDir.isDirectory()) {
            log.error("not a valid directory for svn parent path: '"
                    + svnParentDir.getAbsolutePath() + "'");
        }
        initialize(svnParentDir);
    }
    private static void initialize(File svnParentDir) {
        instance = new IO();
        // get repo path prefix
        log.info("initializing with svnParent='" + svnParentDir.getAbsolutePath() + "'");
        
        RepoContext.init(svnParentDir);
    }
    public static IO get() {
        return instance;
    }
    
    private static final String ATT_REPO = "wandisco.repo";
    public void attachRepo(RepoContext repo, ServletRequest req) {
        req.setAttribute(ATT_REPO, repo);
    }
    public RepoContext getRepo(DavServletRequest req) {
        RepoContext ret = (RepoContext) req.getAttribute(ATT_REPO);
        if (ret == null) {
            throw new RuntimeException(
                    "No repo attached to request '" + req.getMethod() +
                    " " + req.getRequestURI() + "'"
                    );
        }
        return ret;
    }

    public LockManager getLockManager() { return lockManager; }
    
    // assert a.len < b.len
    public static String relPath(String a, String b) {
        if (!b.startsWith(a)) {
            throw new IllegalArgumentException(
                    "'" + b + "' does not start with '" + a + "'"
                    );
            //return null;
        }
        int lena = a.length();
        int lenb = b.length();
        String ret = null;
        if (lena == lenb) {
            ret = "";
        } else {
            ret = b.substring(lena);
        }
        return ret;
    }
    public SVNResource makeResource(DavServletRequest req, String relpath) {
        return makeResource(getRepo(req), relpath);
    }
    public SVNResource makeResource(RepoContext repo, String relpath) {
        String repoPath = repo.getContextPath();
        if (relpath == null || "/".equals(relpath) || "".equals(relpath)) {
            relpath = "/";
        }
        if (relpath.startsWith(repoPath)) {
            relpath = relPath(repoPath, relpath);
        }
        if (relpath.length() > 0 && relpath.charAt(0) == '/') {
            relpath = relpath.substring(1);
        }
        log.debug("makeResource() pruned to '" + relpath + "' from repoPath=" + repoPath);
        DavResourceLocator l = locatorFactory.createResourceLocator(repo.getContextPath(), relpath);
        return new SVNResource(repo, relpath, l);
    }
    
    public String joinPath(String parent, String subpath) {
        return Path.join(parent, subpath);
        /*
        StringBuilder sb = new StringBuilder(parent);
        int plen = parent.length();
        if (plen > 0 && parent.charAt(plen - 1) != '/') {
            sb.append('/');
        }
        if (subpath.length() > 0 && subpath.charAt(0) == '/') {
            sb.append(subpath.substring(1));
        } else {
            sb.append(subpath);
        }
        return sb.toString();
        */
    }
    public void close(Object o) {
        try {
            if (o == null) {
                // nothing
            } else if (o instanceof InputStream) {
                ((InputStream)o).close();
            } else if (o instanceof OutputStream) {
                ((OutputStream)o).close();
            }
        } catch (IOException ignore) { }
    }
    public void stream(InputStream is, OutputStream os)
    throws IOException {
        byte[] buf = new byte[4096];
        int r;
        try {
           while ((r = is.read(buf)) > 0)
               os.write(buf, 0, r);
        } finally {
            close(is);
            close(os);
        }
    }
}
